#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdarg.h>

#ifdef WIN32
#include <crtdbg.h>
#else
#include <arpa/inet.h>
#endif

#include "nblob.h"
#include "ncrypt_types.h"
#include "nmalloc.h"

//////////////////////////////////////////////////////////////////////////////////////
// NBLOB struct Manuplation

const NBLOB NCRYPT_NULL_BLOB = { 0, 0 };

NBLOB* DupBlob(const NBLOB* pSrcBlob)
{
	_ASSERTE(pSrcBlob != NULL);
	NBLOB* pDstBlob = (NBLOB*)malloc(sizeof(NBLOB));
	AllocBlob(pDstBlob, pSrcBlob->cSize);
	CopyMemory(pDstBlob->pData, pSrcBlob->pData, pSrcBlob->cSize);
	return pDstBlob;
}

DWORD CopyBlob(NBLOB* pDstBlob, const NBLOB* pSrcBlob)
{
	_ASSERTE(pDstBlob != NULL);
	_ASSERTE(pSrcBlob != NULL);
	FreeBlob(pDstBlob);
	AllocBlob(pDstBlob, pSrcBlob->cSize);
	CopyMemory(pDstBlob->pData, pSrcBlob->pData, pSrcBlob->cSize);
	return pSrcBlob->cSize;
}

void AssignBlob(NBLOB* pBlob, BYTE* pData, DWORD cSize)
{
	pBlob->pData = pData;
	pBlob->cSize = cSize;
}

void AllocBlob(NBLOB* pBlob, DWORD cSize)
{
	pBlob->cSize = cSize;
	pBlob->pData = (BYTE*)malloc(cSize);
}

void FreeBlob(NBLOB* pBlob)
{
	_ASSERTE(pBlob != NULL);
	if ( pBlob->pData ) free(pBlob->pData);
	*pBlob = NULL_BLOB;
}

void DestroyBlob(NBLOB* pBlob)
{
	_ASSERTE(pBlob != NULL);
	if ( pBlob->pData ) free(pBlob->pData);
	free(pBlob);
}

NBLOB* MergeBlob(int n, ...)
{
	NBLOB* pMerged = (NBLOB*)malloc(sizeof(NBLOB));
	ZeroMemory(pMerged, sizeof(NBLOB));
	BYTE* offset = 0;
	va_list argptr;
	int i = 0;

	va_start(argptr, n);

	for ( i = 0 ; i < n ; i++ )
	{
		NBLOB* pBlob = va_arg(argptr, NBLOB*);
		pMerged->cSize += sizeof(DWORD) + pBlob->cSize;
	}

	AllocBlob(pMerged, pMerged->cSize);
	offset = pMerged->pData;

	va_start(argptr, n);

	for ( i = 0 ; i < n ; i++ )
	{
		NBLOB* pBlob = va_arg(argptr, NBLOB*);
		DWORD cSize = htonl(pBlob->cSize);
		CopyMemory(offset, &cSize, sizeof(DWORD));
		offset += sizeof(DWORD);
		CopyMemory(offset, pBlob->pData, pBlob->cSize);
		offset += pBlob->cSize;
	}

	return pMerged;
}

NBLOB* SplitBlob(NBLOB* pMerged, int *pLen)
{
	if ( !pMerged || !pMerged->cSize || !pMerged->pData ) 
		return 0;
	
	DWORD remains = pMerged->cSize;
	BYTE* offset = pMerged->pData;
	DWORD cSize = 0;
	int n = 0;

	for ( ; remains > 0 ; n++ )
	{
		if ( remains < sizeof(DWORD) )
			break;

		cSize = ntohl(*(DWORD*)offset);
		remains -= sizeof(DWORD);
		offset += sizeof(DWORD);

		if ( remains < cSize )
			break;

		remains -= cSize;
		offset += cSize;
	}

	if ( pLen ) *pLen = n;

	NBLOB* pSplited = (NBLOB*)malloc(sizeof(NBLOB)*(n+1));
	ZeroMemory(pSplited, sizeof(NBLOB)*(n+1));
	offset = pMerged->pData;

	for ( int i = 0 ; i < n ; i++ )
	{
		pSplited[i].cSize = ntohl(*(DWORD*)offset);
		offset += sizeof(DWORD);
		pSplited[i].pData = offset;
		offset += pSplited[i].cSize;
	}

	return pSplited;
}

int CompareBlob(NBLOB* pBlob1, NBLOB* pBlob2)
{
	if ( !pBlob1 && !pBlob2 ) return 0;
	if ( !pBlob1 && pBlob2 ) return -1;
	if ( pBlob1 && !pBlob2 ) return 1;

	int diff = 0;
	int min = pBlob1->cSize < pBlob2->cSize ? pBlob1->cSize : pBlob2->cSize;

	for ( int i = 0 ; i < min ; i++ )
	{
		diff = pBlob1->pData[i] - pBlob2->pData[i];
		if ( diff ) return diff;
	}

	if ( pBlob1->cSize == pBlob2->cSize ) return 0;
	if ( pBlob1->cSize < pBlob2->cSize ) return -1;
	
	return 1;
}

int GetBlobArrayLen(NBLOB* pBlob)
{
	int i = 0;
	for ( ; pBlob && pBlob[i].pData ; i++ );
	return i;
}

BOOL operator==(NBLOB a, NBLOB b)
{
	if ( a.cSize != b.cSize ) return FALSE;
	if ( a.pData == b.pData ) return TRUE;
	if ( a.pData == NULL || b.pData == NULL ) return FALSE;
	for ( int i = 0 ; i < (int)a.cSize ; i++ )
	{
		if ( a.pData[i] != b.pData[i] ) return FALSE;
	}

	return TRUE;
}

BOOL operator!=(NBLOB a, NBLOB b)
{
	return !(a == b);
}

